package com.learn.sunday.auth.bean;

import com.alibaba.fastjson.JSON;
import com.learn.sunday.auth.compont.SpringUtils;
import com.learn.sunday.auth.service.UserService;
import com.learn.sunday.common.service.utils.DigestUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Data
public class DigestBean {


    private UserService userService = SpringUtils.getBean(UserService.class);
    /**
     * 二次认证的用户名
     */
    private final String username;
    /**
     * 表示Web服务器中受保护文档的安全域（比如公司财务信息域和公司员工信息域），用来指示需要哪个域的用户名和密码
     */
    private final String realm;
    /**
     * 服务端向客户端发送质询时附带的一个随机数，这个数会经常发生变化。客户端计算密码摘要时将其附加上去，使得多次生成同一用户的密码摘要各不相同，用来防止重放攻击
     */
    private final String nonce;
    /**
     * 请求的url
     */
    private final String uri;
    /**
     * 这是由用户代理软件计算出的一个字符串，以证明用户知道口令
     */
    private final String response;
    /**
     * 保护质量，包含auth（默认的）和auth-int（增加了报文完整性检测）两种策略，（可以为空，但是）不推荐为空值
     */
    private final String qop;
    /**
     * 二nonce计数器，是一个16进制的数值，表示同一nonce下客户端发送出请求的数量。例如，在响应的第一个请求中，客户端将发送“nc=00000001”。这个指示值的目的是让服务器保持这个计数器的一个副本，以便检测重复的请求
     */
    private final String nc;
    /**
     * 客户端随机数，这是一个不透明的字符串值，由客户端提供，并且客户端和服务器都会使用，以避免用明文文本。这使得双方都可以查验对方的身份，并对消息的完整性提供一些保护
     */
    private final String cnonce;


    private final String section212response;
    /**
     * 过期时间
     */
    private long nonceExpiryTime;


    /**
     * Digest realm="myRealm", qop="auth", nonce="MTcxOTIzNjUzNzA3ODpjMzE2MDIzMTRkMGZlOThmYWI4NjZlY2Y0MTQyYjk5OQ=="
     * @param header
     */
    public DigestBean(String header) {
        this.section212response = header.substring(7);
        String[] split = this.section212response.split(",");
        Map<String,String> headMap = new HashMap<>();
        for (String s : split) {
            if (!s.contains("=")) {
                continue;
            }
            String[] array = s.split("=",2);
                headMap.put(array[0].replace("\"","").trim(),
                        array[1].replace("\"","").trim());
        }
        this.username = headMap.get("username");
        this.realm = headMap.get("realm");
        this.nonce = headMap.get("nonce");
        this.uri = headMap.get("uri");
        this.response = headMap.get("response");
        this.qop = headMap.get("qop");
        this.nc = headMap.get("nc");
        this.cnonce = headMap.get("cnonce");
        log.info("解析后的用户信息是:{}",JSON.toJSONString(this));

    }

    public boolean validateAndDecode(String entryPointKey, String expectedRealm) {
        // Check all required parameters were supplied (ie RFC 2069)
        if ((this.username == null) || (this.realm == null) || (this.nonce == null)
                || (this.uri == null) || (this.response == null)) {
            log.error("认证信息错误不包含用户信息或者信息不全!");
            return false;
        }
        UserDetails userDetails = userService.loadUserByUsername(username);
        if (userDetails == null) {
            log.error("查询用户认证信息错误!");
            return false;
        }

        // Check all required parameters for an "auth" qop were supplied (ie RFC 2617)
        if ("auth".equals(this.qop)) {
            if ((this.nc == null) || (this.cnonce == null)) {
            }
        }

        // Check realm name equals what we expected
        if (!expectedRealm.equals(this.realm)) {
        }

        // Check nonce was Base64 encoded (as sent by DigestAuthenticationEntryPoint)
        try {
            Base64.getDecoder().decode(this.nonce.getBytes());
        }
        catch (IllegalArgumentException e) {

        }

        // Decode nonce from Base64
        // format of nonce is:
        // base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
        String nonceAsPlainText = new String(Base64.getDecoder().decode(this.nonce.getBytes()));
        String[] nonceTokens = StringUtils
                .delimitedListToStringArray(nonceAsPlainText, ":");

        if (nonceTokens.length != 2) {

        }

        // Extract expiry time from nonce

        try {
            this.nonceExpiryTime = new Long(nonceTokens[0]);
        }
        catch (NumberFormatException nfe) {

        }

        // Check signature of nonce matches this expiry time
        String expectedNonceSignature = DigestUtils
                .md5Hex(this.nonceExpiryTime + ":" + entryPointKey);
//
        if (!expectedNonceSignature.equals(nonceTokens[1])) {
            return false;
        }
        return true;
    }

}
